package geradorDeArquivoArff;

import arquivo.Pegatexto;
import java.io.File;
import java.util.ArrayList;
import java.sql.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JOptionPane;
import redeneural.ClassificarInstancia;
import redeneural.Treinamento;



/**
 * Esta e uma classe para criacao de codigos no formato arff apartir de
 * bandos de dados mysql.
 * @author Heloina Alves
 */
public class RegistrosValores {

    /**
     * Url para coneccao ao banco de dados mysql, no formato:
     * jdbc:mysql://host/database
     */
    private String url;
    /**
     * Usuario para acessar o banco de dados.
     */
    private String usuario;
    /**
     * Senha para acessar o banco de dados.
     */
    private String senha;
    /**
     * Coneccao com o banco de dados.
     */
    private Connection conn;
    /**
     * Statement responsavel por executar as consultas sql.
     */
    private Statement stmt;
    /**
     * ResultSet responsavel por guardar os resultados das consultas sql
     */
    private ResultSet rs;
    /*
     * ClassificarInstancia responsavel por classificar as instancias do .arff
     */
    private ClassificarInstancia classificador;
    /*
     * Treinamento responsavel por treinar a rede neural
     */
    private Treinamento treinamento;
    /*
     * Numero de instancias classificadas corretamente
     */
    private int corretas;
    /**
     * Construtor da classe. Responsvel por fazer a coneccao com o banco de dados.
     * @param String host - endereco do banco de dados.
     * @param String nomeDoBanco - nome da base de dados.
     * @param String usuario - usuario para conectar ao banco de dados.
     * @param String senha - senha para conectar ao banco de dados.
     */
    public RegistrosValores(String host, String nomeDoBanco, String usuario, String senha) {

        this.url = "jdbc:mysql://" + host + "/" + nomeDoBanco;
        this.usuario = usuario;
        this.senha = senha;

        conn = null;

        //Tenta conectar a base de dados.
        try {
            Class.forName("com.mysql.jdbc.Driver").newInstance();
            conn = DriverManager.getConnection(url, usuario, senha);
        } catch (SQLException ex) {
            System.out.println("SQLException: " + ex.getMessage() + "<br>");
            System.out.println("SQLState: " + ex.getSQLState() + "<br>");
            System.out.println("VendorError: " + ex.getErrorCode() + "<br>");
        } catch (Exception e) {
            System.out.println("Problemas ao tentar conectar com o banco de dados");
        }
    }

    /**
     * Metodo que retorna os nomes dos campos de uma tabela passada por parametro.
     * @param String tabela - nome da tabela que queremos pegar o nome dos campos.
     * @return ArrayList<String> - array de nomes dos campos.
     */
    public ArrayList<String> getCampos(String tabela) {
        ArrayList<String> result = new ArrayList<String>();

        //conn e a conexao com o banco de dados.
        if (conn != null) {
            try {
                //statement para execucao das consultas.
                stmt = conn.createStatement();
                //consultar todos os campos da tabela retornando apenas um resultado.
                String query = "SELECT * FROM " + tabela + " LIMIT 0,1";
                //executando a consulta.
                rs = stmt.executeQuery(query);
                //obtendo as informacoes da tabela.
                ResultSetMetaData md = rs.getMetaData();

                //numero de campos
                int numeroCampos = md.getColumnCount();

                //percorre os campos e insere-os no array result
                for (int i = 1; i <= numeroCampos; i++) {
                    result.add(md.getColumnName(i));
                }
            } catch (SQLException ex) {
                Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return result;
    }

    /**
     * Metodo que retorna os tipos dos campos de uma tabela passada por parametro.
     * @param String tabela - nome da tabela que queremos pegar o tipo dos campos.
     * @return ArrayList<String> - array de tipos dos campos.
     */
    public ArrayList<String> getTipos(String tabela) {
        ArrayList<String> result = new ArrayList<String>();

        //conn e a conexao com o banco de dados.
        if (conn != null) {
            try {
                //statement para execucao das consultas.
                stmt = conn.createStatement();
                //consultar todos os campos da tabela retornando apenas um resultado.
                String query = "SELECT * FROM " + tabela + " LIMIT 0,1";
                //executando a consulta.
                rs = stmt.executeQuery(query);
                //obtendo as informacoes da tabela.
                ResultSetMetaData md = rs.getMetaData();

                //numero de campos.
                int numeroCampos = md.getColumnCount();

                //percorre os campos e insere-os no array result.
                for (int i = 1; i <= numeroCampos; i++) {
                    if (md.getColumnTypeName(i).equals("VARCHAR")) {
                        //verifica se o tipo do campo e varchar.
                        //nome do campo.
                        String campo = md.getColumnName(i);
                        //consulta para obter os elementos repetidos deste campo.
                        String query2 = "SELECT " + campo + " FROM " + tabela + " GROUP BY " + campo;
                        //executa a consulta sql.
                        rs = stmt.executeQuery(query2);
                        //chave, que contem os elementos repetidos no formato {ele1,ele2,ele3}
                        String chave = "{";
                        while (rs.next()) {
                            chave += "'" + rs.getString(campo) + "'";
                            chave += ", ";
                        }
                        chave = chave.substring(0, chave.length()-2);
                        chave += "}";
                        result.add(chave);
                    } else if ((md.getColumnTypeName(i).equals("INT")) || (md.getColumnTypeName(i).equals("DOUBLE"))) {
                        //verifica se o tipo do campo e int ou double.
                        //se for int ou double retorna numeric.
                        result.add("NUMERIC");
                    } else {
                        //se nao for nenhum dos casos acima, retorna o tipo vindo da tabela mysql.
                        result.add(md.getColumnTypeName(i));
                    }
                }
            } catch (SQLException ex) {
                Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return result;
    }

    /**
     * Metodo que retorna os tipos dos campos de uma tabela passada por parametro.
     * @param String tabela - nome da tabela que queremos pegar o tipo dos campos.
     * @param ArrayList<String> campos - nomes dos campos para retornar os tipos.
     * @return ArrayList<String> - array de tipos dos campos.
     */
    public ArrayList<String> getTipos(String tabela, ArrayList<String> campos) {
        //transforma o arraylist campos na string queryCampos que e a lista dos 
        //campos no formato: "campo1,campo2,campo3"
        String queryCampos = "";
        for (int i = 0; i < campos.size(); i++) {
            queryCampos += campos.get(i);
            if (i != (campos.size() - 1)) {
                queryCampos += ", ";
            }
        }

        ArrayList<String> result = new ArrayList<String>();

        //conn e a conexao com o banco de dados.
        if (conn != null) {
            try {
                //statement para execucao das consultas.
                stmt = conn.createStatement();
                //consultar os campos da tabela passados por parametro retornando apenas um resultado.
                String query = "SELECT " + queryCampos + " FROM " + tabela + " LIMIT 0,1";
                //executando a consulta.
                rs = stmt.executeQuery(query);
                //obtendo as informacoes da tabela.
                ResultSetMetaData md = rs.getMetaData();

                //numero de campos.
                int numeroCampos = md.getColumnCount();

                //percorre os campos e insere-os no array result.
                for (int i = 1; i <= numeroCampos; i++) {
                    if (md.getColumnTypeName(i).equals("VARCHAR")) {
                        //verifica se o tipo do campo e varchar.
                        //nome do campo.
                        String campo = md.getColumnName(i);
                        //consulta para obter os elementos repetidos deste campo.
                        String query2 = "SELECT " + campo + " FROM " + tabela + " GROUP BY " + campo;
                        //executa a consulta sql.
                        rs = stmt.executeQuery(query2);
                        //chave, que contem os elementos repetidos no formato {ele1,ele2,ele3}
                        String chave = "{";
                        while (rs.next()) {
                            if (!chave.contains(rs.getString(campo))) {
                                chave += "'" + rs.getString(campo) + "'";
                                chave += ", ";
                            }
                        }
                        chave = chave.substring(0, chave.length()-2);
                        chave += "}";
                        result.add(chave);
                    } else if ((md.getColumnTypeName(i).equals("INT")) || (md.getColumnTypeName(i).equals("DOUBLE"))) {
                        //verifica se o tipo do campo e int ou double.
                        //se for int ou double retorna numeric.
                        result.add("NUMERIC");
                    } else {
                        //se nao for nenhum dos casos acima, retorna o tipo vindo da tabela mysql.
                        result.add(md.getColumnTypeName(i));
                    }
                }
            } catch (SQLException ex) {
                Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return result;
    }

    public ArrayList<String> getTipos2(String tabela, ArrayList<String> campos, String consultaSql) {


        ArrayList<String> result = new ArrayList<String>();

        //conn e a conexao com o banco de dados.
        if (conn != null) {
            try {
                //statement para execucao das consultas.
                stmt = conn.createStatement();
                //consultar os campos da tabela passados por parametro retornando apenas um resultado.
                String query;
                query = consultaSql;
                //executando a consulta.
                rs = stmt.executeQuery(query);
                //obtendo as informacoes da tabela.
                ResultSetMetaData md = rs.getMetaData();

                //numero de campos.
                int numeroCampos = md.getColumnCount();

                //percorre os campos e insere-os no array result.
                for (int i = 1; i <= numeroCampos; i++) {
                    if (md.getColumnTypeName(i).equals("VARCHAR")) {
                        //verifica se o tipo do campo e varchar.
                        //nome do campo.
                        String campo = md.getColumnName(i);
                        String query2;
                        if (campo.equals("name")) {
                            query2 = "SELECT name FROM jos_users WHERE id!=62 GROUP BY name;";
                        }else {
                            String query11 = "SELECT id FROM jos_community_fields WHERE NAME = '"+campo+"';";
                            ResultSet rs2 = stmt.executeQuery(query11);
                            rs2.next();
                            int id = rs2.getInt("id");
                            //consulta para obter os elementos repetidos deste campo.
                            query2 = "SELECT  MAX(IF(b.field_id="+id+",b.value, NULL)) AS '"+campo+"' "
+"FROM jos_community_users a "
+"LEFT JOIN jos_community_fields_values b ON a.userid=b.user_id "
+"LEFT JOIN jos_community_fields f ON f.id = b.field_id "
+"LEFT JOIN jos_users u ON u.id = a.userid "
+"WHERE a.userid != 62 "
+"GROUP BY a.userid, '"+campo+"' "
+"ORDER BY u.name, a.userid;";
                        }
                        

                        
                        //executa a consulta sql.
                        rs = stmt.executeQuery(query2);
                        //chave, que contem os elementos repetidos no formato {ele1,ele2,ele3}
                        String chave = "{";
                        while (rs.next()) {
                            if (!chave.contains(rs.getString(campo))) {
                                chave += "'" + rs.getString(campo) + "'";
                                chave += ", ";
                            }
                        }
                        chave = chave.substring(0, chave.length()-2);
                        chave += "}";
                        result.add(chave);
                    } else if ((md.getColumnTypeName(i).equals("INT")) || (md.getColumnTypeName(i).equals("DOUBLE"))) {
                        //verifica se o tipo do campo e int ou double.
                        //se for int ou double retorna numeric.
                        result.add("NUMERIC");
                    } else {
                        //se nao for nenhum dos casos acima, retorna o tipo vindo da tabela mysql.
                        result.add(md.getColumnTypeName(i));
                    }
                }
            } catch (SQLException ex) {
                Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        return result;
    }

    /**
     * Metodo para a geracao do codigo arff.
     * @param String tabela - nome da tabela para criar o arquivo arff, utilizando
     * todos os campos da tabela.
     * @return String - codigo arff referente a tabela com todos os campos.
     */
    public String geraArff(String tabela) {
        //relation contendo o nome da tabela.
        String arff = "@relation " + tabela + "\n\n";
        //lista de todos os campos desta tabela.
        ArrayList<String> campos = this.getCampos(tabela);
        //lista de todos os tipos relacionados aos campos desta tabela.
        ArrayList<String> tiposCampos = this.getTipos(tabela);

        //define um @attribute com o nome do campo e o tipo, para cada campo da tabela.
        for (int i = 0; i < campos.size(); i++) {
            arff += "@attribute " + campos.get(i) + " " + tiposCampos.get(i) + "\n";
        }

        //inicio da definicao dos dados com o @data
        arff += "\n";
        arff += "@data\n";

        //consulta para obter todos os campos da tabela.
        String query = "SELECT * FROM " + tabela;
        try {
            //executa a consulta sql.
            rs = stmt.executeQuery(query);
            //obter as informacoes da tabela.
            ResultSetMetaData md = rs.getMetaData();
            //percorre todos os resultados da consulta.
            while (rs.next()) {
                //percorre todos os campos de cada resultado.
                for (int i = 1; i <= md.getColumnCount(); i++) {
                    if (md.getColumnTypeName(i).equals("VARCHAR")) {
                        //se o tipo do campo for varchar, coloque o conteudo deste campo entre aspas simples ''
                        arff += "'" + rs.getString(campos.get(i - 1)) + "'";
                    } else if (md.getColumnTypeName(i).equals("INT")) {
                        //se o tipo do campo for int, retorne o valor desta variavel.
                        arff += rs.getInt(campos.get(i - 1));
                    } else if (md.getColumnTypeName(i).equals("DOUBLE")) {
                        //se o tipo do campo for double, retorne o valor desta variavel.
                        arff += rs.getDouble(campos.get(i - 1));
                    } else {
                        //se o tipo do campo nao for listado, retorne '?'
                        arff += "?";
                    }
                    if (i != md.getColumnCount()) {
                        arff += ",";
                    }
                }
                arff += "\n";
            }
        } catch (SQLException ex) {
            Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
        }

        return arff;
    }

    /**
     * Metodo para a geracao do codigo arff.
     * @param String tabela - nome da tabela para criar o arquivo arff.
     * @param ArrayList<String> campos - campos para criar o arquivo arff.
     * @return String - codigo arff referente a tabela com todos os campos.
     */
    public String geraArff(String tabela, ArrayList<String> campos) {
        //transforma o arraylist campos na string queryCampos que e a lista dos
        //campos no formato: "campo1,campo2,campo3"
        String queryCampos = "";
        for (int i = 0; i < campos.size(); i++) {
            queryCampos += campos.get(i);
            if (i != (campos.size() - 1)) {
                queryCampos += ", ";
            }
        }
        //relation contendo o nome da tabela.
        String arff = "@relation " + tabela + "\n\n";
        //lista de todos os tipos relacionados aos campos desta tabela.
        ArrayList<String> tiposCampos = this.getTipos(tabela, campos);

        //define um @attribute com o nome do campo e o tipo, para cada campo da tabela.
        for (int i = 0; i < campos.size(); i++) {
            arff += "@attribute " + campos.get(i) + " " + tiposCampos.get(i) + "\n";
        }

        //inicio da definicao dos dados com o @data
        arff += "\n";
        arff += "@data\n";

        //consulta para obter todos os campos da tabela.
        String query = "SELECT " + queryCampos + " FROM " + tabela;
        try {
            //executa a consulta sql.
            rs = stmt.executeQuery(query);
            //obter as informacoes da tabela.
            ResultSetMetaData md = rs.getMetaData();
            //percorre todos os resultados da consulta.
            while (rs.next()) {
                //percorre todos os campos de cada resultado.
                for (int i = 1; i <= md.getColumnCount(); i++) {
                    if (md.getColumnTypeName(i).equals("VARCHAR")) {
                        //se o tipo do campo for varchar, coloque o conteudo deste campo entre aspas simples ''
                        arff += "'" + rs.getString(campos.get(i - 1)) + "'";
                    } else if (md.getColumnTypeName(i).equals("INT")) {
                        //se o tipo do campo for int, retorne o valor desta variavel.
                        arff += rs.getInt(campos.get(i - 1));
                    } else if (md.getColumnTypeName(i).equals("DOUBLE")) {
                        //se o tipo do campo for double, retorne o valor desta variavel.
                        arff += rs.getDouble(campos.get(i - 1));
                    } else {
                        //se o tipo do campo nao for listado, retorne '?'
                        arff += "?";
                    }
                    if (i != md.getColumnCount()) {
                        arff += ",";
                    }
                }
                arff += "\n";
            }
        } catch (SQLException ex) {
            Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
        }
        return arff;
    }

        /**
     * Metodo para a geracao do codigo arff.
     * @param String tabela - nome da tabela para criar o arquivo arff.
     * @param ArrayList<String> campos - campos para criar o arquivo arff.
     * @return String - codigo arff referente a tabela com todos os campos.
     */
    public String geraArff(String tabela, ArrayList<String> campos, String consultaSql) {
        //transforma o arraylist campos na string queryCampos que e a lista dos
        //campos no formato: "campo1,campo2,campo3"
        ArrayList<String> camposfixos = new ArrayList<String>();
        camposfixos.add("User ID");
        camposfixos.add("Name");
        camposfixos.add("graduação");
        camposfixos.add("mestrado");
        camposfixos.add("doutorado");

        campos = camposfixos;

        String queryCampos = "";
        for (int i = 0; i < campos.size(); i++) {
            queryCampos += campos.get(i);
            if (i != (campos.size() - 1)) {
                queryCampos += ", ";
            }
        }
        //relation contendo o nome da tabela.
        String arff = "@relation " + tabela + "\n\n";
        //lista de todos os tipos relacionados aos campos desta tabela.
        ArrayList<String> tiposCampos = this.getTipos2(tabela, campos, consultaSql);

        //define um @attribute com o nome do campo e o tipo, para cada campo da tabela.
        for (int i = 0; i < campos.size(); i++) {
            arff += "@attribute '" + campos.get(i) + "' " + tiposCampos.get(i) + "\n";
        }

        //inicio da definicao dos dados com o @data
        arff += "\n";
        arff += "@data\n";

        //consulta para obter todos os campos da tabela.
        String query = consultaSql;
        try {
            //executa a consulta sql.
            rs = stmt.executeQuery(query);
            //obter as informacoes da tabela.
            ResultSetMetaData md = rs.getMetaData();
            //percorre todos os resultados da consulta.
            while (rs.next()) {
                //percorre todos os campos de cada resultado.
                for (int i = 1; i <= md.getColumnCount(); i++) {
                    if (md.getColumnTypeName(i).equals("VARCHAR")) {
                        //se o tipo do campo for varchar, coloque o conteudo deste campo entre aspas simples ''
                        arff += "'" + rs.getString(campos.get(i - 1)) + "'";
                    } else if (md.getColumnTypeName(i).equals("INT")) {
                        //se o tipo do campo for int, retorne o valor desta variavel.
                        arff += rs.getInt(campos.get(i - 1));
                    } else if (md.getColumnTypeName(i).equals("DOUBLE")) {
                        //se o tipo do campo for double, retorne o valor desta variavel.
                        arff += rs.getDouble(campos.get(i - 1));
                    } else {
                        //se o tipo do campo nao for listado, retorne '?'
                        arff += "?";
                    }
                    if (i != md.getColumnCount()) {
                        arff += ",";
                    }
                }
                arff += "\n";
            }
        } catch (SQLException ex) {
            Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
        }
        return arff;
    }

    public ArrayList<String> getTabelas() {
        ArrayList<String> result = new ArrayList<String>();
        DatabaseMetaData dbmd;
        try {
            dbmd = conn.getMetaData();

            // Specify the type of object; in this case we want tables
            String[] types = {"TABLE"};
            ResultSet resultSet = dbmd.getTables(null, null, "%", types);
            // Get the table names
            while (resultSet.next()) {
                result.add(resultSet.getString(3));
            }
        } catch (SQLException ex) {
            Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
        }
        return result;
    }




     public ArrayList<String> Dados(String tabela) {
        ArrayList<String> result = new ArrayList<String>();
        String query = "SELECT * FROM " + tabela;

        try {
            //executa a consulta sql.
            rs = stmt.executeQuery(query);
           //obter as informacoes da tabela.
            ResultSetMetaData md = rs.getMetaData();
            //percorre todos os resultados da consulta.
            while (rs.next()) {
                //percorre todos os campos de cada resultado.
                for (int i = 1; i <= md.getColumnCount(); i++){
                    result.add(rs.getString(i));
                }// fim do for
                }
        }catch(SQLException ex) {
            Logger.getLogger(RegistrosValores.class.getName()).log(Level.SEVERE, null, ex);
        }
    return result;

    }


     public String classificarUmaInstancia(){

     try{
         return classificador.classificarUmaInstancia();
     }catch(Exception e){
        return "Nao foi possivel realizar a chamada da função 1\nErro: " + e;
     }
     }


    public String classificarAll(String tabela){
        classificador = new ClassificarInstancia(treinamento);
     try{
         return classificador.classificarAll(this.Dados(tabela));
     }catch(Exception e){
       return "Nao foi possivel realizar a chamada da função n\nErro: " + e;
     }
     }


     public String treinarRedeNeural(String tabela,double taxa,double erro,int epoca,String camada){

        try {
           File arquivo = Pegatexto.escreverArquivo(geraArff(tabela), tabela);
            //JOptionPane.showMessageDialog(null, "Criou o arquivo "+arquivo);
            treinamento = new Treinamento(arquivo,taxa,erro,epoca,camada);
            corretas = treinamento.Treinar();
            return "sucesso";
        } catch (Exception ex) {
           return ex.getMessage();
        }

    }

     public String treinarRedeNeural(String tabela, ArrayList<String> campos, String consultaSql,double taxa,double erro,int epoca,String camada){

        try {
           File arquivo = Pegatexto.escreverArquivo(geraArff(tabela, campos, consultaSql), tabela);
            //JOptionPane.showMessageDialog(null, "Criou o arquivo "+arquivo);
            treinamento = new Treinamento(arquivo,taxa,erro,epoca,camada);
            corretas = treinamento.Treinar();
            return "sucesso";
        } catch (Exception ex) {
           return ex.getMessage();
        }

    }

}
